//参考
//https://www.runoob.com/cprogramming/c-arrays.html
//https://blog.csdn.net/qq_15144867/article/details/93341737
//https://blog.csdn.net/zqixiao_09/article/details/50877383

#include "ls.h"

#define MASKS_LEN 10
#define SEP "\t"

//视情况采用stat或lstat
int (*stat_f)(const char *, struct stat *) = stat;

const char ERR_CHAR = ' ';

char fileType(mode_t mode)
{
    switch (mode & S_IFMT)
    {
    case S_IFREG:
        return '-';
    case S_IFDIR:
        return 'd';
    case S_IFCHR:
        return 'c';
    case S_IFBLK:
        return 'b';
    case S_IFIFO:
        return 'f';
    case S_IFLNK:
        return 'l';
    case S_IFSOCK:
        return 's';
    default:
        //unknown
        return ERR_CHAR;
    }
}

char filePermission(mode_t mode, mode_t mask)
{
    if (mode & mask == 0)
        return '-';
    else
    {
        switch (mask)
        {
        case S_IRUSR:
        case S_IRGRP:
        case S_IROTH:
            return 'r';
        case S_IWUSR:
        case S_IWGRP:
        case S_IWOTH:
            return 'w';
        case S_IXUSR:
        case S_IXGRP:
        case S_IXOTH:
            return 'x';
        default:
            return ERR_CHAR;
        }
    }
}

const size_t MODE_STR_LEN = 12;
const mode_t MASKS[MASKS_LEN] = {
    0,
    //用户（所有者）
    S_IRUSR,
    S_IWUSR,
    S_IXUSR,
    //组
    S_IRGRP,
    S_IWGRP,
    S_IXGRP,
    //其他
    S_IROTH,
    S_IWOTH,
    S_IXOTH};
const char PERMISSION_STR[]="Erwxrwxrwx";
void filePermission2(char*modeStr,mode_t mode,size_t idx);
inline void filePermission2(char*modeStr,mode_t mode,size_t idx)
{
    modeStr[idx]=(mode&MASKS[idx])?PERMISSION_STR[idx]:'-';
}
void outputFileMode(mode_t mode)
{
    char modeStr[MODE_STR_LEN] /*  = {'\0'} */;
    modeStr[0] = fileType(mode);
    size_t i;
    for (i = 1; i < MASKS_LEN; ++i){
        //modeStr[i] = filePermission(mode, MASKS[i]);
        filePermission2(modeStr,mode,i);
    }
    modeStr[MASKS_LEN] = '\0';
    printf("%s"SEP, modeStr);
}

void print_type(mode_t mode)
{
    printf("%c",fileType(mode));
}
void print_perm(mode_t mode)
{
    char modeStr[MODE_STR_LEN];
    size_t i;
    for(i=1;i<MASKS_LEN;++i)filePermission2(modeStr,mode,i);
    modeStr[MASKS_LEN]='\0';
    printf("%s"SEP,modeStr+1);
}

void print_link(unsigned long link)
{
    printf("%lu"SEP,link);
}

void print_size(long size)
{
    printf("%ld"SEP,size);
}

void print_time(time_t t)
{
    char*s=ctime(&t);
    //abort();
    char*s2=strchr(s,'\n');
    if(s2!=NULL)*s2='\0';
    printf("%s"SEP,s);
}

void print_filename(const struct dirent*curdp)
{
    printf("%s\n",curdp->d_name);
}

void print_usrname(uid_t uid)
{
    printf("%s"SEP, getpwuid(uid)->pw_name);
}

void print_grname(gid_t gid)
{
    printf("%s"SEP, getgrgid(gid)->gr_name);
}

//文件类型 权限 硬链接数 所有者用户名 所有者所在组用户名 文件大小 最后修改时间
void ls(bool is_all, bool is_almost_all, bool is_link_dereference)
{
    if(is_link_dereference)stat_f=lstat;
    // 获取当前路径信息
    char dirPath[PATH_MAX];
    if (getcwd(dirPath, PATH_MAX) != NULL){
        printf("%s\n", dirPath);
    }
    else
    {
        perror("get current path");
        return;
    }
        

    DIR *catalog = opendir(dirPath);
    if (catalog == NULL){
        printf("open directory failed\n");
        perror(dirPath);
    }
    else
    {
        //目录项
        struct dirent *currentdp;
        for (currentdp = readdir(catalog); currentdp != NULL; currentdp = readdir(catalog))
        {
            if (currentdp->d_name[0] != '.' || (is_almost_all || is_all))
            {
                if (!is_all && currentdp->d_name[0] == '.' && (strcmp(currentdp->d_name, ".") == 0 || strcmp(currentdp->d_name, "..") == 0)) continue;
                //输出该文件（夹）的所有信息
                //printf("%s\n",currentdp->d_name);
                /* char absPath[PATH_MAX];
                strcpy(absPath,dirPath);
                strcat(absPath,"/");
                strcat(absPath,currentdp->d_name); */
                struct stat currentstat;
                /* if(strcmp(currentdp->d_name,".")==0||strcmp(currentdp->d_name,"..")==0)continue;
                else  */
                if (stat_f(currentdp->d_name, &currentstat) != 0)
                {
                    //出错
                    printf("get stat error\n");
                    perror(currentdp->d_name);
                    //无需释放
                    //free(currentdp);
                    //break;
                    continue;
                }
                else
                {
                    //输出文件（夹）属性
                    //outputFileMode(currentstat.st_mode);
                    print_type(currentstat.st_mode);
                    print_perm(currentstat.st_mode);
                    //printf("%lu\t%ld\t", currentstat.st_nlink, currentstat.st_size);
                    print_link(currentstat.st_nlink);
                    print_size(currentstat.st_size);
                    print_usrname(currentstat.st_uid);
                    print_grname(currentstat.st_gid);
                    //printf("%s\t%s\n", ctime(&currentstat.st_mtim.tv_sec), currentdp->d_name);
                    print_time(currentstat.st_mtime);
                    print_filename(currentdp);
                }
                //无需释放
                //free(currentdp);
            }
        }
    }
    closedir(catalog);
}
